package per.liuyayiweb;

import per.liuyayiweb.exception.UnSupportMethodException;
import per.liuyayiweb.util.Resource;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.net.URLDecoder;

public class Server {
    private final static int HTTP_PORT = 128;
    private final static int CODE_OK = 200;
    private final static int CODE_NOT_FOUND = 404;
    private final static int CODE_CREATE = 201;
    private final static String OK = "OK";
    private final static String NOT_FOUND = "Not Found";
    private final static String CREATE = "Created";
    private Map<String, String> headers;
    private Map<String, String> parasMap;

    private final static String TYPE_PNG = "Content-Type: image/png";
    private final static String TYPE_JPEG = "Content-Type: image/jpeg";
    private final static String TYPE_DOC = "Content-Type: application/msword";
    private final static String TYPE_HTML = "Content-Type: text/html";
    private final static String TYPE_TXT = "Content-Type: text/plain";

    private final static String SERVER_PATH = "D:/Clone/Zs-Team-Learn/liuyayi/Learn-Project-3/server";
    private int port;
    private ServerSocket serverSocket;
    private InputStream request;
    private OutputStream response;

    public Server() throws IOException {
        this.port = HTTP_PORT;
        serverSocket = new ServerSocket(HTTP_PORT);
    }

    public Server(int port) throws IOException {
        this.port = port;
        serverSocket = new ServerSocket(port);
    }

    public void listen() {
        while (true) {
            Socket client;
            try {
                client = serverSocket.accept();
                if (client == null) continue;
                System.out.println("客户端 " + client.getInetAddress().toString() + " 已连接");
                request = client.getInputStream();
                response = client.getOutputStream();
                String firstLine = readLine();
                System.out.println(firstLine);
                Method method = getMethod(firstLine);

                switch (method) {  // 处理常见的几种请求
                    case GET:
                        handleGet(firstLine, response);
                        break;
                    case POST:
                        headers = new LinkedHashMap<>();
                        while (true) {
                            String line = readLine();
                            if ("".equals(line)) {
                                System.out.println(headers);
                                break;
                            }
                            String[] data = line.split(":");
                            headers.put(data[0].trim(), data[1].trim());

                        }
                        if (this.headers.containsKey("Content-Length")) {
                            int contentLength = Integer.parseInt(this.headers.get("Content-Length"));
                            String contentType = this.headers.get("Content-Type");//根据Content-Type判断是否为form表单
                            if ("application/x-www-form-urlencoded".equals(contentType)) {
                                System.out.println("开始处理Post请求中form表单内容!");//通过流读取消息正文中的所有字节
                                try {
                                    byte[] data = new byte[contentLength];
                                    int total = 0;
                                    while(total < contentLength) {
                                        data[total++] = (byte) request.read();
                                    }
                                    String line = URLDecoder.decode(new String(data, "ISO8859-1"), "utf-8");
                                    System.out.println(line);
                                    parasMap = new LinkedHashMap<>();
                                    String[] querys = line.split("&");
                                    for (int i = 0; i < querys.length; i++) {
                                        String[] data1 = querys[i].split("=");
                                        parasMap.put(data1[0], data1[1]);
                                    }

                                } catch (Exception e) {
                                    e.printStackTrace();
                                    throw e;
                                }
                            } else if ("application/json".equals(contentType)) {
                                try {
                                    byte[] data = new byte[contentLength];
                                    int total = 0;
                                    while(total < contentLength) {
                                        data[total++] = (byte) request.read();
                                    }
                                    String line = URLDecoder.decode(new String(data, "ISO8859-1"), "utf-8");
                                    System.out.println(line);
                                    parasMap = new LinkedHashMap<String, String>();
                                    line = line.replace("{", ",");
                                    line = line.replace("}", ",");
                                    line = line.replace("\"", "");
                                    String[] querys = line.split(",");
                                    for (int i = 0; i < querys.length; i++) {
                                        String[] data1 = querys[i].split(":");
                                        parasMap.put(data1[0], data1[1]);
                                    }

                                } catch (Exception e) {
                                    e.printStackTrace();
                                    throw e;
                                }
                            }
                        }
                        handlePost(parasMap,firstLine, response);
                        listen();
                        break;
                    case PUT:
                        System.out.println("收到一个PUT请求，已忽略");
                        break;
                    case DELETE:
                        System.out.println("收到一个DELETE请求，已忽略");
                        break;
                    default:
                        break;
                }
            } catch (IOException | UnSupportMethodException e) {
                e.printStackTrace();
            } finally {
                Resource.closeOutputStream(response);
                Resource.closeInputStream(request);
            }
        }
    }
    private String readLine() throws IOException {
        StringBuilder builder = new StringBuilder();
        int c;
        while ((c = request.read()) != -1) {
            if (c == '\n')
                break;
            if (c == '\r')
                continue;
            builder.append((char)c);
        }
        return builder.toString();
    }
    /**
     * @param firstLine GET /test.jpg HTTP/1.1
     * @param out       outputStream
     */
    public void handleGet(String firstLine, OutputStream out) {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
        StringBuilder responseHeader = new StringBuilder("HTTP/1.1 ");
        FileInputStream fileResponse = null;
        byte[] data = null;
        try {
            String path = firstLine.substring(firstLine.indexOf('/') + 1, firstLine.lastIndexOf('/') - 5); //  GET /test.txt HTTP/1.1
            if ("".equals(path.trim())) path = "index.html"; //  / -> /index.html
            String fullPath = SERVER_PATH + path;
            String fileFormate = path.substring(path.lastIndexOf('.') + 1); // test.jpg -> jpg
            File file = new File(fullPath);
            if (!file.exists() || file.isDirectory()) {
                //HTTP/1.1 404 NOT FOUND
                responseHeader.append(CODE_NOT_FOUND).append(" ").append(NOT_FOUND).append(System.lineSeparator());
            } else {
                responseHeader.append(CODE_OK).append(" ").append(OK).append(System.lineSeparator());
                //判断文件类型，添加适当内容
                if (fileFormate.equalsIgnoreCase("png")) {
                    responseHeader.append(TYPE_PNG).append(System.lineSeparator());
                } else if (fileFormate.equalsIgnoreCase("jpg") || fileFormate.equalsIgnoreCase("jpeg")) {
                    responseHeader.append(TYPE_JPEG).append(System.lineSeparator());
                } else if (fileFormate.equalsIgnoreCase("doc") || fileFormate.equalsIgnoreCase("docx")) {
                    responseHeader.append(TYPE_DOC).append(System.lineSeparator());
                } else if (fileFormate.equalsIgnoreCase("htm") || fileFormate.equalsIgnoreCase("html")) {
                    responseHeader.append(TYPE_HTML).append(System.lineSeparator());
                } else if (fileFormate.equalsIgnoreCase("txt")) {
                    responseHeader.append(TYPE_TXT).append(System.lineSeparator());
                }
                //响应日期
                responseHeader.append("Date: ").append(new Date()).append(System.lineSeparator());
                //添加文件长度
                responseHeader.append("Content-Length: ").append(file.length()).append(System.lineSeparator());
                responseHeader.append(System.lineSeparator());
                out.flush();
                //写入文件
                fileResponse = new FileInputStream(file);
                data = new byte[fileResponse.available()];
                System.out.println("Read length " + fileResponse.read(data));
            }
        } catch (IOException e) {
            e.printStackTrace();
            responseHeader.append(CODE_NOT_FOUND).append(" ").append(NOT_FOUND).append(System.lineSeparator());
            responseHeader.append(System.lineSeparator());
        } finally {
            try {
                out.write(responseHeader.toString().getBytes());
                if (data != null) {
                    out.write(data);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            Resource.closeOutputStream(out);
            Resource.closeInputStream(fileResponse);
            System.out.println(responseHeader.toString());
            System.out.println("<<<<<<<<<<<<<<<<<<<<");
        }
    }

    public void handlePost(Map<String, String> parasMap,String firstLine, OutputStream out) {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
        StringBuilder responseHeader = new StringBuilder("HTTP/1.1 ");
        byte[] data = null;
        responseHeader.append(CODE_OK).append(" ").append(OK).append(System.lineSeparator());
        responseHeader.append("Date: ").append(new Date()).append(System.lineSeparator());
        responseHeader.append(System.lineSeparator());
        System.out.println(responseHeader.toString());
        System.out.println("<<<<<<<<<<<<<<<<<<<<");
        String name = parasMap.get("username");
        String pwd = parasMap.get("password");
        System.out.println(name);
        System.out.println(pwd);
        handleGet(firstLine, out);

    }

    /**
     * 获取请求方法
     * GET / HTTP/1.1   method path protocol_version
     *
     * @param firstLine HTTP 请求的首行
     * @return Method
     */
    private Method getMethod(String firstLine) throws UnSupportMethodException {
        if (firstLine == null) return null;
        String method = firstLine.substring(0, firstLine.indexOf('/')).trim(); // -》 GET
        if ("get".equalsIgnoreCase(method)) return Method.GET;
        else if ("post".equalsIgnoreCase(method)) return Method.POST;
        else if ("put".equalsIgnoreCase(method)) return Method.PUT;
        else if ("delete".equalsIgnoreCase(method)) return Method.DELETE;
        else throw new UnSupportMethodException("不支持的方法 " + method);
    }

    /**
     * 将 BufferedReader 转换成 String
     *
     * @param reader origin reader
     * @return reader content
     * @throws IOException when reader is unreadable
     */
    private String bufferedReader2String(BufferedReader reader) throws IOException {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append(System.lineSeparator());
        }
        reader.reset();  //重置指针
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        Server Server = new Server();
        Server.listen();
    }
}

enum Method {
    GET,
    PUT,
    POST,
    DELETE
}